package cn.blnp.net.flowable.listen.global.events;

import cn.blnp.net.flowable.config.GlobalEventListener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.*;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

/**
 * <h3></h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/21 16:58
 */
@Slf4j
@SpringBootTest
public class GlobalEventListenerConfig {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;
    @Resource
    private ManagementService managementService;

    /**
     *  <h2>配置方式</h2>
     *  <h3>1、通过流程定义文件配置事件监听器</h3>
     *  <p>
     *    &emsp;&emsp;在流程定义中同样可以配置事件监听器，不过通过这种方式配置的事件监听器只会监听与该流程定义相关的事件，以及该流程定义上发起的所有流程实例的事件。监听器可以使用以下
     *    3种方式配置实现：
     *    <ul>
     *        <li>通过class属性进行全类名定义；</li>
     *        <li>通过delegateExpression属性引用实现了监听器接口的表达式</li>
     *        <li>使用throwEvent属性及其额外属性指定抛出的BPMN事件类型。</li>
     *    </ul>
     *    <pre>
     *        {@code
     *             <process id="eventListenersProcess">
     *                 <extensionElements>
     *                     <!-- 通过class属性进行全类名定义 -->
     *                     <flowable:eventListener class="com.bpm.example.eventlistener.demo.listener.GlobalEventListener" />
     *                     <!-- 通过delegateExpression属性引用实现了监听器接口的表达式 -->
     *                     <flowable:eventListener delegateExpression="${globalEventListener}" events="TASK_CREATED,TASK_COMPLETED" />
     *                 </extensionElements>
     *             </process>
     *        }
     *    </pre>
     *    &emsp;&emsp;第一个监听器通过class属性指定监听器的全类名定义，会接收所有类型的事件。第二个监听器通过delegateExpression属性通过UEL配置委托表达式${globalEventListener}，只接收
     *    用户任务创建和用户任务完成的事件。其中，globalEventListener是实现了 {@link org.flowable.common.engine.api.delegate.event.FlowableEventListener}的类的一个实例，可以添加
     *    到流程变量中。Flowable在执行表达式时会查询与其同名的流程变量，并执行其onEvent()方法。如果集成了Spring，它也可以是Spring容器中的Bean。除此之外还可以：
     *    <pre>
     *        {@code
     *              <process id="eventListenersProcess">
     *                  <extensionElements>
     *                      <!-- 通过class属性进行全类名定义，指定了entityType -->
     *                      <flowable:eventListener class="com.bpm.example.eventlistener.demo.listener.GlobalEventListener" entityType="task" />
     *                      <!-- 通过delegateExpression属性引用实现了监听器接口的表达式，指定了entityType -->
     *                      <flowable:eventListener delegateExpression="${globalEventListener}" events="ENTITY_CREATED" entityType="task" />
     *                  </extensionElements>
     *                  <!-- 其他元素省略 -->
     *              </process>
     *        }
     *    </pre>
     *    &emsp;&emsp;两者均配置了entityType属性。第一个监听器监听了task实体的所有事件，第二个监听器监听了task实体的指定事件。entityType支持的值包括<u>attachment、comment、execution、
     *    identity-link、job、process-instance、process-definition和task</u>。
     *  </p>
     **/
    @Test
    public void test() {

    }

    /**
     *  <h2>2、通过抛出bpmn事件方式实现</h2>
     *  <h3>示例一：</h3>
     *  <pre>
     *      {@code
     *          <process id="testEventListeners">
     *              <extensionElements>
     *                  <flowable:eventListener throwEvent="signal" signalName="MyProcessInstanceScopeSignal" events="TASK_CREATED" />
     *              </extensionElements>
     *          </process>
     *      }
     *  </pre>
     *  <p>
     *   配置的事件监听器会在监听到TASK_CREATED事件时，向流程实例内部抛出名称为MyProcessInstanceScopeSignal的信号事件。通过这种方式配置的事件监听器实现类是
     *   {@link org.flowable.engine.impl.bpmn.helper.SignalThrowingEventListener}，它实现了{@link org.flowable.common.engine.api.delegate.event.FlowableEventListener}
     *   接口。
     *  </p>
     *  <h3>示例二：</h3>
     *  <pre>
     *      {@code
     *          <process id="testEventListeners">
     *              <extensionElements>
     *                  <flowable:eventListener throwEvent="message" messageName="MyMessage" events="TASK_ASSIGNED" />
     *              </extensionElements>
     *          </process>
     *      }
     *  </pre>
     *  <p>
     *      代码配置的事件监听器会在监听到TASK_ASSIGNED事件时，向流程实例内部抛出名称为MyMessage的消息事件。通过这种方式配置的事件监听器实现类是
     *      {@link org.flowable.engine.impl.bpmn.helper.MessageThrowingEventListener}，它实现了{@link org.flowable.common.engine.api.delegate.event.FlowableEventListener}接口。
     *  </p>
     *  <h3>示例三：</h3>
     *  <pre>
     *      {@code
     *          <process id="testEventListeners">
     *              <extensionElements>
     *                  <flowable:eventListener throwEvent="error" errorCode="CancelledError" events="PROCESS_CANCELLED" />
     *              </extensionElements>
     *          </process>
     *      }
     *  </pre>
     *  <p>
     *      代码配置的事件监听器会在监听到PROCESS_CANCELLED事件时，向流程实例内部抛出错误码为CancelledError的错误事件。通过这种方式配置的事件监听器实现类是
     *      {@link org.flowable.engine.impl.bpmn.helper.ErrorThrowingEventListener}，它实现了{@link org.flowable.common.engine.api.delegate.event.FlowableEventListener}接口。
     *  </p>
     *
     *  <h3 style="color:red;">注意事项：</h3>
     *  <ol>
     *      <li>
     *          事件监听器只能声明在process元素下作为extensionElements的子元素，而不能定义在其他流程元素（如节点）下。
     *      </li>
     *      <li>
     *          通过delegateExpression属性配置的表达式无法访问execution上下文，这与其他表达式（如排他网关）不同。它只能引用定义在工作流引擎配置的beans属性中声明的Bean，或者使用Spring（但未使用工作流引擎配置的beans属性）
     *          中所有实现了监听器接口的Spring Bean或其他实现了监听器接口的实例。
     *      </li>
     *      <li>
     *          在使用监听器的class属性时，只会创建一个实例，因此需要确保监听器实现类不依赖成员变量，或确保多线程、上下文的使用安全。
     *      </li>
     *      <li>
     *          如果events属性配置了不合法的事件类型，或者配置了不合法的throwEvent值，则会在流程定义部署时抛出异常，导致部署失败。
     *      </li>
     *      <li>
     *          如果class或delegateExpression属性指定了不合法的值，如不存在的类、不存在的Bean引用，或代理类没有实现监听器接口，那么在该流程定义的事件分发到这个监听器时，会抛出异常，因此需要确保引用的类在classpath中，
     *          并且保证表达式能够解析为有效的监听器实例。
     *      </li>
     *  </ol>
     *
     **/
    @Test
    public void testThrow() {

    }

    /**
     *  <h2>3、调用API动态添加事件监听器</h2>
     **/
    @Test
    public void testApi() {
        //注意，通过这种方式在运行阶段添加的监听器工作流引擎重启后即会消失。
        runtimeService.addEventListener(new GlobalEventListener());

        //移除监听器
        runtimeService.removeEventListener(new GlobalEventListener());
    }

    /**
     *  <h2>4、通过配置文件配置</h2>
     *  <h3>Spring 项目配置</h3>
     *  <pre>
     *      {@code
     *
     *      }
     *  </pre>
     **/
    @Test
    public void test2() {

    }
}
